import random
import math
from xml.etree import ElementTree

import pyglet

import cocos
from cocos.actions import *

from gamelib import actions

class Spell(object):
    # Spells have a number of attributes
    # name          -- the spell's name (also its label)
    # type          -- the school of magic
    # description   -- a longer description of its effect
    # mana_cost     -- the mana cost
    # action_cost   -- the action points cost
    # action_class  -- the Action class to use
    # for_friendly  -- the spell is intended to be cast on friendlies
    # requires_target -- whether a ranged spell requires a character target
    for_friendly = False

    def difficulty(self, level, character, cell, other):
        '''Determine the base chance for this spell to succeed when cast by
        "character" on "cell" or "other" in "level".
        '''
        return 1

    def do(self, level, character, cell, other):
        '''Make this spell do something as cast by "character" on "cell"
        or "other" in "level".

        Default implementation is to use the mana points.
        '''
        return character.use_mana_points(self.cost)

    def check(self, action):
        '''The Action class wishes to make sure this spell is OK with
        being cast. The action has a character, cell and other (character)
        attribute. If the spell is ranged there is also a distance
        attribute.
        '''
        return True

    def animate_attack(self, layer, attack):
        layer.do(CallFunc(layer.done))

    def to_xml(self, root):
        m = ElementTree.SubElement(root, 'spell', type=self.type,
            name=self.name)
        m.tail = '\n'

def from_xml(tag):
    '''Load the item from an XML file.
    '''
    type = tag.get('type')
    name = tag.get('name')
    return spells[type][name]()

spells = {}
def add_spell(klass):
    spells.setdefault(klass.type, {})[klass.name] = klass

class EnergyBolt(Spell):
    name = 'Energy Bolt'
    type = 'attack'
    description = 'A bolt of magical energy to strike your foe'
    mana_cost = 1
    action_cost = 3
    action_class = actions.RangedMagic
    requires_target = True
    icon = 'effect-energy-bolt.png'

    def difficulty(self, level, character, cell, other):
        # target has a chance to dodge
        if other is None:
            return 1
        return 1 - other.dexterity/20.

    def do(self, level, character, cell, other):
        skill = character.skills.get('spell_attack', 0)
        damage = random.randint(1, 7) + skill
        print 'EnergyBolt:', damage, 'damage to', other
        other.apply_damage(damage)
        return True

    def animate_attack(self, layer, attack):
        i1, j1 = attack.character.position
        x1, y1 = attack.level.ground.get_cell(i1, j1).center

        i2, j2 = attack.other.position
        x2, y2 = attack.level.ground.get_cell(i2, j2).center

        angle = math.degrees(math.atan2(y1-y2, x2-x1))

        i = pyglet.resource.image('effect-energy-bolt.png')
        s = cocos.sprite.Sprite(i, (x1, y1), rotation=angle)
        layer.add(s)

        # need constant-speed 1024 pixels per second not constant-time animation here
        distance = math.sqrt((x2-x1)**2 + (y2-y1)**2)
        t = distance / 1024.
        s.do(MoveTo((x2, y2), t) + CallFunc(layer.done))
add_spell(EnergyBolt)

class FlameHands(Spell):
    name = 'Flame Hands'
    type = 'attack'
    description = 'Your hands burn with magical fury!'
    mana_cost = 1
    action_cost = 3
    action_class = actions.MeleeMagic
    icon = 'effect-flame-hands.png'

    def difficulty(self, level, character, cell, other):
        # target has a chance to dodge
        if other is None:
            return 1
        return 1 - other.dexterity/20.

    def do(self, level, character, cell, other):
        skill = character.skills.get('spell_attack', 0)
        damage = random.randint(2, 9) + skill
        print 'FlameHands:', damage, 'damage to', other
        other.apply_damage(damage)
        return True

    def animate_attack(self, layer, attack):
        i1, j1 = attack.character.position
        x1, y1 = attack.level.ground.get_cell(i1, j1).center

        i2, j2 = attack.other.position
        x2, y2 = attack.level.ground.get_cell(i2, j2).center

        angle = math.degrees(math.atan2(y1-y2, x2-x1))

        pos = (x1 + (x2-x1)/2, y1 + (y2-y1)/2)

        i = pyglet.resource.image('effect-flame-hands.png')
        i.anchor_x = 0
        s = cocos.sprite.Sprite(i, pos, rotation=angle)
        layer.add(s)

        fade = Accelerate(FadeIn(.25))
        s.do(fade + Reverse(fade) + CallFunc(layer.done))
add_spell(FlameHands)

class Kill(Spell):
    name = 'Kill'
    type = 'attack'
    description = 'You kill your enemy through sheer force of will'
    mana_cost = 100
    action_cost = 5
    action_class = actions.RangedMagic
    requires_target = True

    def difficulty(self, level, character, cell, other):
        # fails for newbies
        return 0

    def do(self, level, character, cell, other):
        print 'Kill:', other
        other.apply_damage(other.health)
        return True
add_spell(Kill)


class Heal(Spell):
    name = 'Heal'
    type = 'healing'
    description = 'You heal your target'
    mana_cost = 1
    action_cost = 2
    action_class = actions.MeleeMagic
    requires_target = True
    for_friendly = True
    icon = 'effect-heal.png'

    def difficulty(self, level, character, cell, other):
        # always succeeds
        return 1

    def do(self, level, character, cell, other):
        skill = character.skills.get('spell_attack', 0)
        healing = random.randint(2, 9) + skill
        print 'Heal:', other, healing
        other.apply_damage(-healing)
        return True

    def animate_attack(self, layer, attack):
        i, j = attack.other.position
        x, y = attack.level.ground.get_cell(i, j).center

        fade = (Accelerate(FadeIn(.25)) + FadeOut(.25)) | ScaleTo(2, .5)

        i = pyglet.resource.image('effect-heal.png')
        for n, (dx, dy) in enumerate([(-16, -16), (16, 24), (24, -40), (8, 8)]):
            pos = (x + dx, y + dy)
            s = cocos.sprite.Sprite(i, pos, opacity=0)
            layer.add(s)
            anim = Delay(n * .25) + fade
            if n == 3:
                anim = anim + CallFunc(layer.done)
            s.do(anim)
add_spell(Heal)

